using Domain.Entities;
using NHibernate;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System;

namespace Domain.Services
{
    public static class SearchService
    {
        public static IList<Topic> SearchForTopicsByTerms(
            string terms,
            bool requireAllTerms,
            List<int> targetCategoryIds,
            List<int> targetForumIds
        )
        {
            StringBuilder query = new StringBuilder();
            query.Append("select distinct topic from Topic topic inner join topic.Messages message where (");
            List<string> termsPrepared = PrepareTerms(terms);
            AddMessageConstraints(query, termsPrepared, requireAllTerms);
            AddTopicSubjectConstraints(query, termsPrepared, requireAllTerms);
            query.Append(")");
            AddForumContraints(query, targetCategoryIds, targetForumIds);
            
            ISession session = NHibernateHelper.Instance.CurrentSession;
            return session.CreateQuery(query.ToString()).List<Topic>();
        }

        private static void AddTopicSubjectConstraints(StringBuilder query, List<string> termsPrepared, bool requireAllTerms)
        {
            query.Append(" or (");
            bool isFirst = true;
            foreach (string term in termsPrepared)
            {
                if (!isFirst)
                {
                    query.Append(requireAllTerms ? " and" : " or");
                }
                query.Append(" topic.Subject like '");
                query.Append(term);
                query.Append("'");
                isFirst = false;
            }
            query.Append(")");
        }

        public static IList<Message> SearchForMessagesInTopic(
            Topic topic,
            string terms,
            bool requireAllTerms
        )
        {
            StringBuilder query = new StringBuilder();
            query.Append("from Message message where message.Topic.Id = ");
            query.Append(topic.Id);
            query.Append(" and ");

            AddMessageConstraints(query, PrepareTerms(terms), requireAllTerms);

            ISession session = NHibernateHelper.Instance.CurrentSession;
            return session.CreateQuery(query.ToString()).List<Message>();
        }

        private static void AddForumContraints(StringBuilder query, List<int> targetCategoryIds, List<int> targetForumIds)
        {
            bool hasCategoryIds = targetCategoryIds.Count > 0;
            bool hasForumIds = targetForumIds.Count > 0;
            if (hasCategoryIds || hasForumIds)
            {
                query.Append(" and (");
            }
            if (hasCategoryIds)
            {
                query.Append(" topic.Forum.Category.Id in (");
                query.Append(NHibernateHelper.ConvertToCSV(targetCategoryIds));
                query.Append(")");
            }
            if (hasForumIds)
            {
                if (hasCategoryIds)
                {
                    query.Append(" or");
                }
                query.Append(" topic.Forum.Id in (");
                query.Append(NHibernateHelper.ConvertToCSV(targetForumIds));
                query.Append(")");
            }
            if (hasCategoryIds || hasForumIds)
            {
                query.Append(")");
            }
        }

        private static void AddMessageConstraints(
            StringBuilder query,
            List<string> termsPrepared, 
            bool requireAllTerms
        )
        {
            query.Append("(");
            bool isFirst = true;
            foreach (string term in termsPrepared)
            {
                if (!isFirst)
                {
                    query.Append(requireAllTerms ? " and" : " or");
                }
                query.Append(" message.Text like '");
                query.Append(term);
                query.Append("'");
                isFirst = false;
            }
            query.Append(")");
        }

        internal static List<string> PrepareTerms(string terms)
        {
            List<string> termsPrepared = new List<string>();
            Array.ForEach<string>(
                Regex.Split(terms, @"\s+"),
                delegate(string term) { termsPrepared.Add("%" + term + "%"); }
            );
            return termsPrepared;
        }
    }
}
